/**
 * $Revision: 1.0 $
 * $Author: Eric Yang $
 * $Date: Aug 21, 2009 10:28:24 PM $
 *
 * Author: Eric Yang
 * Date  : Aug 21, 2009 10:28:24 PM
 *
 */
package com.gooex.domain.base;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.c4j.encode.Encoder;

import com.gooex.domain.base.emun.OrderStatus;
import com.gooex.domain.common.Region;
import com.gooex.domain.organization.User;


/**
 * Group Order Object<br>
 * <br>
 * Order status: SAVED --> INVITING --> QUOTING --> VOTING --> CONFIRMED --> CANCELED --> CLOSED<br>
 * <br>
 * <table border="1">
 * <tr>
 * <th> Status</th>
 * <th> Description</th>
 * </tr>
 * <tr>
 * <td> SAVED</td>
 * <td> customer can create a order,but not release it,the order will temporarily saved</td>
 * </tr>
 * <tr>
 * <td> INVITING</td>
 * <td> After customer release the order,the order status change to INVITING,<br>
 * another customers and venders can join the order.</td>
 * </tr>
 * <tr>
 * <td> QUOTING</td>
 * <td> If the number of customers joined in a order is over the minimun number, the order status will changed into QUOTING at the end of the day.<br>
 * <br>
 * If the status of a order is QUOTING, other customers can't join in the order any more.<br>
 * and the venders can still join in and quote for all customers' request.</td>
 * </tr>
 * <tr>
 * <td> VOTING </td>
 * <td> if all venders have quoted or over the quoting deadline,the order's status will changed to VOTING.<br>
 * the customers joined in the order will be noticed to vote for the vendors . <br>
 * </td>
 * </tr>
 * <tr>
 * <td>CONFIRMED</td>
 * <td> The voting will end if all customers have voted or over the voting deadline.<br>
 * System will auto chose the highest voting rate vendor as the successful bid vendor,<br>
 * then the order status change to CONFIRMED,and the vendor can see the information of the customers and trade with them. </td>
 * </tr>
 * <tr>
 * <td>CANCELED</td>
 * <td> 以下情况订单状态会变为CANCELED:
 * <ul>
 * <li>在INVITING的时候,负责人将订单取消.</li>
 * <li>在INVITING的订单超过了inviteDeadline,但是参与的用户还没有达到订单最少参与人数,订单会自动被取消(系统会在晚上12点检查所有订单).<br>
 * 注:为解决订单被自动取消的问题,负责人可以提前修改订单过期时间.</li>
 * </ul>
 * </td>
 * </tr>
 * <tr>
 * <td> CLOSED</td>
 * <td> 状态为CONFIRMED的订单,商家和用户交易完成之后,可以关闭此订单,订单状态转变为CLOSED.</td>
 * </tr>
 * </table>
 * 
 * @author Eric Yang
 * @version 1.0
 */
public class Order implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -9052212153413119321L;

	/**
	 * 订单编号(规则:Category.id(6) + yyMMdd(6) + random number(8))
	 */
	private String id;

	/** 标题 */
	private String title;

	/** 创建人 */
	private User createUser;

	/**
	 * 负责人<br>
	 * 最初为创建人，创建人可以指定其他参与人为负责人.订单相关操作由负责人操作
	 */
	private User responsibleUser;

	/** 订单状态 */
	private OrderStatus status;

	/** 订单最少参与人数 */
	private int minCustomerNum = 0;

	/** 订单最少需求量 */
	private long minProductNum = 0;

	/**
	 * 订单最多参与人数 <br>
	 * Default -1 means no max number limited.
	 */
	private int maxCustomerNum = -1;

	/** 订单最少供应商参与人数 */
	private int minVendorNum = 0;

	/**
	 * 订单最多供应商参与人数 <br>
	 * Default -1 means no max number limited.
	 */
	private int maxVendorNum = -1;

	/** 创建者填写的对商品的建议价格 */
	private BigDecimal proposalPrice;

	/**
	 * 参与截止时间 <br>
	 * 在参与用户没有达到最多的情况下，根据截至时间结束订单参与
	 */
	private Date inviteDeadline;

	/**
	 * 报价截止时间 <br>
	 * 可以在创建订单的时候设定，也可以在邀请结束后修改，根据截至时间结束报价
	 */
	private Date quoteDeadline;

	/**
	 * 投票截止时间 <br>
	 * 可以在创建订单的时候设定，也可以在商家报价结束后修改，根据截至时间结束投票
	 */
	private Date voteDeadline;

	/** 订单确认时间 */
	private Date comfirmDate;

	/**
	 * 订单关闭时间<br>
	 * 需要负责人手动关闭,如果一定时间没有关闭,则系统将其关闭
	 */
	private Date closedTime;

	/** 订单创建时间 */
	private Date creationTime;

	/**
	 * 订单类别，也是商品的类别
	 */
	private Category category;

	/**
	 * 对应商品,用商品去限制该订单，也就是客户选择的商品都属于此商品，但是具体参数可以不同，比如颜色、型号。所以商品不应该是具体到具体型号的某一个商品，而是一类商品。 (说明:此分类方法参考了taobao的分类方法,原来添加参数值得方法感觉很难实现和操作,有什么看法请提出)
	 */
	private Product product;

	/**
	 * 用户数量<br>
	 * 增加统计字段值,必要时更新此字段值,减轻db查询压力
	 */
	private int customerCount = 0;

	/**
	 * 供应商数量<br>
	 * 增加统计字段值,必要时更新此字段值,减轻db查询压力
	 */
	private int vendorCount = 0;

	/**
	 * 订购商品数量<br>
	 * 增加统计字段值,必要时更新此字段值,减轻db查询压力
	 */
	private long productCount = 0;

	/** 订单参与的用户 */
	private List<OrderCustomer> customers = new ArrayList<OrderCustomer>();

	/** 订单参与供应商列表 */
	private List<OrderVendor> vendors = new ArrayList<OrderVendor>();

	/** 最终投票获胜的供应商 */
	private OrderVendor votedVendor;

	/** 订单范围客户地域限定 */
	private Region customerRegion;

	/** 供应商地域限定 */
	private Region vendorRegion;

	/** order data updated time */
	private Date updatedTime;

	/* ===========functions============================= */
	/**
	 * @return 联购数量
	 */
	public long getProductNums() {
		long count = 0;
		for (OrderCustomer item : this.customers) {
			for (OrderRequest request : item.getOrderRequests())
				count += request.getCount();
		}

		return count;
	}

	/**
	 * Add a {@link OrderCustomer} in the order if the customer is a new one.
	 * 
	 * @param customer
	 */
	public void addCustomer(OrderCustomer customer) {
		if (!customers.contains(customer)) {
			customers.add(customer);
			customer.setOrder(this);
		}
	}

	/**
	 * Remove a {@link OrderCustomer} from the order if exists.
	 * 
	 * @param customer
	 */
	public void removeCustomer(OrderCustomer customer) {
		if (customers.contains(customer))
			customers.remove(customer);
	}

	/**
	 * Add a {@link OrderVendor} in the order if the vendor is a new one.
	 * 
	 * @param vendor
	 */
	public void addVendor(OrderVendor vendor) {
		if (!vendors.contains(vendor))
			vendors.add(vendor);
	}

	/**
	 * Remove a {@link OrderVendor} from the order if exists.
	 * 
	 * @param vendor
	 */
	public void removeVendor(OrderVendor vendor) {
		if (vendors.contains(vendor))
			vendors.remove(vendor);
	}

	public boolean isSaved() {
		return OrderStatus.SAVED.equals(status);
	}

	public boolean isInviting() {
		return OrderStatus.INVITING.equals(status);
	}

	public boolean isQuoting() {
		return OrderStatus.QUOTING.equals(status);
	}

	public boolean isVoting() {
		return OrderStatus.VOTING.equals(status);
	}

	public boolean isConfirmed() {
		return OrderStatus.CONFIRMED.equals(status);
	}

	public boolean isCanceled() {
		return OrderStatus.CANCELED.equals(status);
	}

	public boolean isClosed() {
		return OrderStatus.CLOSED.equals(status);
	}

	public String getStatusText() {
		return "order_status_" + status.getDesc();
	}

	/**
	 * @param user
	 * @return whether or not the user is the responsor or thr order
	 */
	public boolean isOrderManager(User user) {
		if (responsibleUser == null || user == null)
			return false;
		return responsibleUser.getId().equals(user.getId());
	}

	/**
	 * set deafult order status change deadline
	 * 
	 * @param deadline
	 *            the deadline date
	 */
	public void setDeadline(Date deadline) {
		if (deadline == null)
			return;
		if (isSaved())
			setInviteDeadline(deadline);
		else if (isInviting())
			setQuoteDeadline(deadline);
		else if (isQuoting())
			setVoteDeadline(deadline);
	}

	/**
	 * @return whether or not the order is a right one.
	 */
	public boolean validate() {
		if (category == null || StringUtils.isBlank(category.getId()) || StringUtils.isBlank(title))
			return false;
		if (maxCustomerNum > 0 && minCustomerNum > maxCustomerNum)
			return false;
		if (maxVendorNum > 0 && minVendorNum > maxVendorNum)
			return false;

		return true;
	}

	/**
	 * get all products request number in the order.
	 */
	public Map<Product, Long> getOrderedProductsNum() {
		Map<Product, Long> nums = new HashMap<Product, Long>();
		if (customers != null)
			for (OrderCustomer customer : customers) {
				List<OrderRequest> requests = customer.getOrderRequests();
				for (OrderRequest orderRequest : requests) {
					Product pro = orderRequest.getProduct();
					Long l = nums.get(pro);
					l = (l == null) ? 1 : (l + 1);
					nums.put(pro, l);
				}
			}
		return nums;
	}

	/* ===========setter/getter============================= */

	public Category getCategory() {
		return category;
	}

	public void setCategory(Category category) {
		this.category = category;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getTitle() {
		return title;
	}

	public String getShortTitle() {
		if (title == null)
			return title;
		try {
			byte[] bytes = title.getBytes(Encoder.DEFAULT_CHARSET);
			if (bytes.length > 44) {
				byte[] subarray = ArrayUtils.subarray(bytes, 0, 44);
				String s = new String(subarray, Encoder.DEFAULT_CHARSET);
				return s.substring(0, s.length() - 1) + "...";
			}
		} catch (UnsupportedEncodingException e) {

		}
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public User getCreateUser() {
		return createUser;
	}

	public void setCreateUser(User createUser) {
		this.createUser = createUser;
	}

	public int getMinCustomerNum() {
		return minCustomerNum;
	}

	public void setMinCustomerNum(int minCustomerNum) {
		this.minCustomerNum = minCustomerNum;
	}

	public int getMaxCustomerNum() {
		return maxCustomerNum;
	}

	public void setMaxCustomerNum(int maxCustomerNum) {
		this.maxCustomerNum = maxCustomerNum;
	}

	public int getMinVendorNum() {
		return minVendorNum;
	}

	public void setMinVendorNum(int minVendorNum) {
		this.minVendorNum = minVendorNum;
	}

	public int getMaxVendorNum() {
		return maxVendorNum;
	}

	public void setMaxVendorNum(int maxVendorNum) {
		this.maxVendorNum = maxVendorNum;
	}

	public BigDecimal getProposalPrice() {
		return proposalPrice;
	}

	public void setProposalPrice(BigDecimal proposalPrice) {
		this.proposalPrice = proposalPrice;
	}

	public Date getComfirmDate() {
		return comfirmDate;
	}

	public void setComfirmDate(Date comfirmDate) {
		this.comfirmDate = comfirmDate;
	}

	public Date getClosedTime() {
		return closedTime;
	}

	public void setClosedTime(Date closedTime) {
		this.closedTime = closedTime;
	}

	public Date getCreationTime() {
		return creationTime;
	}

	public void setCreationTime(Date creationTime) {
		this.creationTime = creationTime;
	}

	public List<OrderCustomer> getCustomers() {
		return customers;
	}

	public void setCustomers(List<OrderCustomer> customers) {
		this.customers = customers;
	}

	public List<OrderVendor> getVendors() {
		return vendors;
	}

	public void setVendors(List<OrderVendor> vendors) {
		this.vendors = vendors;
	}

	public User getResponsibleUser() {
		return responsibleUser;
	}

	public void setResponsibleUser(User responsibleUser) {
		this.responsibleUser = responsibleUser;
	}

	public Date getQuoteDeadline() {
		return quoteDeadline;
	}

	public void setQuoteDeadline(Date quoteDeadline) {
		this.quoteDeadline = quoteDeadline;
	}

	public Date getInviteDeadline() {
		return inviteDeadline;
	}

	public void setInviteDeadline(Date inviteDeadline) {
		this.inviteDeadline = inviteDeadline;
	}

	public Date getVoteDeadline() {
		return voteDeadline;
	}

	public void setVoteDeadline(Date voteDeadline) {
		this.voteDeadline = voteDeadline;
	}

	public Product getProduct() {
		return product;
	}

	public void setProduct(Product product) {
		this.product = product;
	}

	public OrderStatus getStatus() {
		return status;
	}

	public void setStatus(OrderStatus status) {
		this.status = status;
	}

	public OrderVendor getVotedVendor() {
		return votedVendor;
	}

	public void setVotedVendor(OrderVendor votedVendor) {
		this.votedVendor = votedVendor;
	}

	public Region getCustomerRegion() {
		return customerRegion;
	}

	public void setCustomerRegion(Region customerRegion) {
		this.customerRegion = customerRegion;
	}

	public Region getVendorRegion() {
		return vendorRegion;
	}

	public void setVendorRegion(Region vendorRegion) {
		this.vendorRegion = vendorRegion;
	}

	public int getCustomerCount() {
		return customerCount;
	}

	public void setCustomerCount(int customerCount) {
		this.customerCount = customerCount;
	}

	public int getVendorCount() {
		return vendorCount;
	}

	public void setVendorCount(int vendorCount) {
		this.vendorCount = vendorCount;
	}

	public long getProductCount() {
		return productCount;
	}

	public void setProductCount(long productCount) {
		this.productCount = productCount;
	}

	public long getMinProductNum() {
		return minProductNum;
	}

	public void setMinProductNum(long minProductNum) {
		this.minProductNum = minProductNum;
	}

	public Date getUpdatedTime() {
		return updatedTime;
	}

	public void setUpdatedTime(Date updatedTime) {
		this.updatedTime = updatedTime;
	}

}
